﻿#include "pch.h"
#include "qgp/qgach.h"
#include "qgp/qgadu.h"
#if _SB_WINDOWS_
#include "extend/sbvalues.h"		// OS 버전을 알아내기 위하여
#endif

#if _SB_WINDOWS_ || _SB_ANDROID_
#	include "freetype/ft2build.h"
#	ifdef _MSC_VER
#		if _SB_64_
#			if _SB_DEBUG_
#				pragma comment(lib, "freetype64_debug.lib")
#			else
#				pragma comment(lib, "freetype64.lib")
#			endif
#		else
#			if _SB_DEBUG_
#				pragma comment(lib, "freetype_debug.lib")
#			else
#				pragma comment(lib, "freetype.lib")
#			endif
#		endif
#	endif
#else
#	include "ft2build.h"
#endif

#include FT_FREETYPE_H
#include FT_SYNTHESIS_H
#include FT_OUTLINE_H
#include FT_SYSTEM_H
#include FT_MODULE_H

#define USEMEMBIND			1
#define CHAR_RESOLUTION		96

namespace _freetype
{
	////////////////////////////////////////////////////////////////////////// 
	// 프리타입 메모리
	class MemRec
	{
	public:
		static void* Alloc(FT_Memory memory, long size)
		{
			return (void*)k_new(size, kbyte);
		}

		static void* Realloc(FT_Memory memory, long cs, long ns, void* block)
		{
			return (void*)k_renew(block, ns, kbyte);
		}

		static void Free(FT_Memory memory, void* block)
		{
			k_delete(block);
		}

		static FT_MemoryRec_ RecDef;
	};

	FT_MemoryRec_ MemRec::RecDef =
	{
		NULL,
		&MemRec::Alloc,
		&MemRec::Free,
		&MemRec::Realloc
	};


	//////////////////////////////////////////////////////////////////////////
	// 글자 키
	struct Key
	{
		kuint			code;
		kint			size;
		kint			style;
	};

	struct KeyHash
	{
		ksize_t operator()(const Key& key) const
		{
			return key.code << 16 | (key.style & 0xFF) << 8 | (key.size & 0xFF);
		}
	};

	struct KeyEq
	{
		bool operator()(const Key& left, const Key& right) const
		{
			return
				left.code == right.code &&
				left.size == right.size &&
				left.style == right.style;
		}
	};


	//////////////////////////////////////////////////////////////////////////
	// 페이스
	class Face
	{
	private:
		FT_Face			_face;
		FT_GlyphSlot	_slot;
		FT_UInt			_index;
		kint			_height;

		bool			_file;
		char*			_name;
		kbyte*			_buf;
		kint			_size;

	public:
		FT_Face GetFace()
		{
			return _face;
		}

		FT_GlyphSlot GetSlot()
		{
			return _slot;
		}

		bool Find(qgStub* stub, const char* filename, bool preload)
		{
			kMount* mnt = stub->GetMount();

			if ((mnt && mnt->IsExist(filename)) || k_file_exist(filename, NULL))
				_name = k_strdup(filename);
			else
			{
#if _SB_WINDOWS_ && _SB_WINDOWS_DESKTOP_
				kwchar sysdir[260];

				if (!GetWindowsDirectory(sysdir, 260 - 1))
					return false;

				kwchar ufn[260], rfn[260];
				k_conv_utf8_to_utf16(ufn, 260 - 1, filename, 0);
				k_snwprintf(rfn, 260 - 1, L"%s\\Fonts\\%s", sysdir, ufn);

				if (!k_file_exist_l(rfn, NULL))
					return false;

				ksize_t len = k_conv_utf16_to_utf8(NULL, 0, rfn, 0);
				_name = k_new(len + 1, char);
				k_conv_utf16_to_utf8(_name, len, rfn, 0);
#elif _SB_ANDROID_
				kchar name[260];
				k_snprintf(name, 260 - 1, "/system/fonts/%s", filename);

				if (!k_file_exist(name, NULL))
					return false;

				_name = k_strdup(name);
#elif _SB_BSD_
				kchar name[260];
				k_snprintf(name, 260 - 1, "/usr/local/lib/X11/fonts/%s", filename);

				if (!k_file_exist(name, NULL))
					return false;

				_name = k_strdup(name);
#elif _SB_LINUX_
				kchar name[260];
				k_snprintf(name, 260 - 1, "/usr/share/fonts/truetype/%s", filename);

				if (!k_file_exist(name, NULL))
					return false;

				_name = k_strdup(name);
#else
#error fix me!
#endif
			}

			if (!preload)
				_file = true;
			else
			{
				_buf = (kbyte*)kMount::FileRead(mnt, _name, &_size);

				if (!_buf)
				{
					k_delete(_name);
					return false;
				}

				_file = false;
				return true;
			}

			return true;
		}

		bool Open(FT_Library ftlib)
		{
			FT_Error err;

			if (!_file)
				err = FT_New_Memory_Face(ftlib, _buf, _size, 0, &_face);
			else
				err = FT_New_Face(ftlib, _name, 0, &_face);

			if (err != 0)
				return false;

			_slot = _face->glyph;

			return true;
		}

		void Close()
		{
			if (_name)
			{
				k_delete(_buf);
				k_delete(_name);
			}

			if (_face)
				FT_Done_Face(_face);
		}
	};


	//////////////////////////////////////////////////////////////////////////
	// 글자
	class Glyph
	{
	public:
		kint			_count;
		kuint			_life;

		tpl::Point		_wh;
		tpl::Point		_bear;
		tpl::Point		_adv;

		kint			_outsize;
		tpl::Point		_drawsize;

		kbyte*			_buf;
		qgTexture*		_tex;

		Face*			_face;

		Key				_key;

	public:
		void AddCount()
		{
			_count++;
		}

		void SetLife(kuint life)
		{
			_life = life;
		}

		bool Build(qgRdh* rdh)
		{
			k_return_value_if_fail(_buf, false);

			kbyte* data = _buf;

			//
			qgTexture* tex = rdh->CreateTexture2(QGCF8_L, _outsize, _outsize, 1, QGRESF_DYNAMIC | QGTEXF_NOMIP, NULL);

			k_return_value_if_fail(tex, false);

			qbTextureMap ms;
			if (!tex->Map(&ms, QGENTF_WRITE))
			{
				tex->Unload();
				return false;
			}

			// 2의 승수가 아닐 수도 있기에 실제 크기를 가지고 있을 껏 qgTexLock은 실제 크기를 반환함
			// 언제나 outsize < drawsize 임
			_drawsize.Set(ms.width, ms.height);

			kbyte* lock = (kbyte*)ms.data;
			memset(lock, 0, ms.pitch * ms.height);

			tpl::Point pt(K_MIN(_outsize, _bear.x), K_MIN(_outsize, _bear.y));

			// 8비트 L -> 세이더로 그리는 경우만 유효
			if (_wh.x > 0)	// 너비가 0보다 작은 경우가 있음!!!
				lock += _wh.x * 1;

			for (kint z = 0; z < pt.y; z++)
			{
				kbyte* pd = lock;

				for (kint i = 0; i < pt.x; i++)
				{
					kbyte tmp = *data;
					*pd++ = tmp;
					++data;
				}

				lock += ms.pitch;
			}

			tex->Unmap();

			//
			_tex = tex;

			return true;
		}

		bool Create(Face* ptr, const Key& key, kint outsize, bool build, qgRdh* rdh)
		{
			// 글자 얻고
			FT_Face face = ptr->GetFace();

			FT_UInt index = FT_Get_Char_Index(face, key.code);
			k_return_value_if_fail(index != 0, false);

			// 읽고
			if (FT_Set_Char_Size(face, 0, key.size, CHAR_RESOLUTION, CHAR_RESOLUTION) != 0 ||
				FT_Load_Glyph(face, index, FT_LOAD_TARGET_NORMAL | FT_LOAD_NO_BITMAP) != 0)
				return false;

			// 효과
			if (face->glyph->format == FT_GLYPH_FORMAT_OUTLINE)
			{
				if (K_OFLAG(key.style, QGFNTS_BOLD))
					FT_Outline_Embolden(&face->glyph->outline, 1 << 6);

				if (K_OFLAG(key.style, QGFNTS_ITALIC))
				{
					static const FT_Matrix ft_mat_italic = {1 << 16, 0x5800, 0, 1 << 16, };
					FT_Outline_Transform(&face->glyph->outline, &ft_mat_italic);
				}
			}

			// 글꼴 그리고
			FT_GlyphSlot slot = ptr->GetSlot();

			if (FT_Render_Glyph(slot, FT_RENDER_MODE_NORMAL) != 0)
				return false;

			// 글자 정보
			_wh.Set(slot->bitmap_left, slot->bitmap_top);
			_bear.Set(slot->bitmap.width, slot->bitmap.rows);
			_adv.Set(slot->advance.x >> 6, 0);

			// 비트맵 뽑고
			if (_bear.x > 0 && _bear.y > 0)
			{
				_buf = k_new(_bear.y * _bear.x, kbyte);
				kbyte* ps = slot->bitmap.buffer;
				kbyte* pd = _buf;

				if (slot->bitmap.pixel_mode == FT_PIXEL_MODE_GRAY)
				{
					for (kint i = 0; i < _bear.y; i++)
					{
						memcpy(pd, ps, _bear.x);
						ps += slot->bitmap.pitch;
						pd += _bear.x;
					}
				}
				else if (slot->bitmap.pixel_mode == FT_PIXEL_MODE_MONO)
				{
					for (int i = 0; i < _bear.y; ++i)
					{
						for (int z = 0; z < _bear.x; ++z)
							pd[z] = ((ps[z / 8] << (z % 8)) & 0x80) ? 0xFF : 0x00;

						ps += slot->bitmap.pitch;
						pd += _bear.x;
					}
				}
				else
				{
					// 헐?!
					return false;
				}
			}
			else
			{
				// 쓸 수 없는 글자 크기, 원래는 사용할 수 없도록 했지만
				// 그리프가 없지만 공백이나 이런거 일 수 있음. buf를 NULL하는 걸로 표시하도록 하자
				//return false;
				_buf = NULL;
			}

			//
			_count = 1;
			_face = ptr;

			_outsize = outsize;

			// 키
			_key = key;

			//
			if (build)
				return Build(rdh);
			else
			{
				_tex = NULL;
				return true;
			}
		}

		void Close()
		{
			kobj_unload(_tex);
			k_delete(_buf);
		}
	};
}

using namespace _freetype;


//////////////////////////////////////////////////////////////////////////
// 캐시 데이터
class qgFreeType::CacheData : public tpl::MemAlloc < >
{
public:
	typedef tpl::unordered_map<Key, Glyph, KeyHash, KeyEq> GlyphHash;
	typedef tpl::vector<Face> FaceArray;

public:
	FT_Library          _lib;
	GlyphHash			_glps;
	FaceArray			_faces;

public:
	CacheData()
	{
		FT_Error err;

#if USEMEMBIND
		err = FT_New_Library(&MemRec::RecDef, &_lib);

		if (err != 0)
		{
			k_tracef(KLV_ERROR, "qg.freetype", "font library error");
			return;
		}

		FT_Add_Default_Modules(_lib);
#else
		err = FT_Init_FreeType(&_lib);

		if (err != 0)
		{
			k_tracef(KLV_ERROR, "qg.freetype", "font initialize error");
			return;
		}
#endif
	}

	~CacheData()
	{
		ClearGlyph();
		ClearTable();

		if (_lib)
#if USEMEMBIND
			FT_Done_Library(_lib);
#else
			FT_Done_FreeType(_lib);
#endif
	}

	void ClearGlyph()
	{
		kForEach(GlyphHash::value_type& p, _glps)
			p.second.Close();
		_glps.clear();
	}

	void ClearTable()
	{
		kForEach(Face& v, _faces)
			v.Close();
		_faces.clear();
	}

	// 키 바인드
	Glyph* Bind(Key& key, kint outsize, kint maxcache, bool maketex, qgRdh* rdh)
	{
		//
		auto it = _glps.find(key);

		if (it != _glps.end())
		{
			Glyph& g = it->second;
			g.AddCount();
			return &g;
		}

		//
		Glyph ng;
		bool isfound = false;

		for (auto fi = _faces.begin(); fi != _faces.end(); ++fi)
		{
			isfound = ng.Create(&(*fi), key, outsize, maketex, rdh);

			if (isfound)
				break;
		}

		if (!isfound)
			return NULL;

		//
		kuint life = k_tick();
		ng.SetLife(life);
		life -= 1000 * 60; // 1분

		if ((kint)_glps.size() >= maxcache)
		{
			auto ki = RemoveOld(100, life);

			if (ki != _glps.end())
				_glps.erase(ki);
			else
			{
				ki = RemoveOld(K_MAX_INT, life);

				if (ki != _glps.end())
					_glps.erase(ki);
			}
		}

		//
		std::pair<Key, Glyph> gitem(key, ng);
		_glps.insert(gitem);

		it = _glps.find(key);

		return &(it->second);
	}

	// 제거하기
	GlyphHash::iterator RemoveOld(kint count, kuint life)
	{
		for (auto it = _glps.begin(); it != _glps.end(); ++it)
		{
			Glyph& g = it->second;

			if (g._key.code < 128)
			{
				// 아스키는 걍 남겨둔다
				continue;
			}

#if _SB_DEBUG_
			if (g._count > count && g._count<100 && g._life>life)
#else
			if (g._count<count && g._life>life)
#endif
				return it;
		}

		return _glps.end();
	}
};


//////////////////////////////////////////////////////////////////////////
// 스크라이브
class qgFreeType::ScribeData : public qgScribeFont
{
	KCH_AUTOCLASS(ScribeData, qgScribeFont, "FreeTypeScribe");
	KCH_FRIEND(qgFreeType);

private:
	qgFreeType*			_font;
	kint				_size;

private:
	ScribeData(qgFreeType* font = NULL)
		: _font(font)
	{
		kobj_load(font);
	}

	~ScribeData()
	{
		kobj_unload(_font);
	}

public:
	K_OVR bool Analyze(kint size, const char* text)
	{
		k_return_value_if_fail(_font, false);
		k_return_value_if_fail(text, false);

		_chs.clear();

		_size = size;

		qgFreeType::CacheData* data = _font->_cache;

		kint savesize = _font->_size;
		_font->SetSize(size);

		const tpl::Point& step = _font->_step;
		kint bsize = size;
		kint gsize = _font->_glyphsize;
		kint osize = _font->_outsize;
		kint cache = _font->_maxcache;

		Key key;
		key.size = gsize;
		key.style = 0;

		kGuardSpinLock(_font->_lock)
		{
			for (const char* t = text; *t != '\0';)
			{
				kint ch = k_utf8ccnc(t);
				t = k_utf8_char_next(t);

				if (ch == ' ')
					_chs.push_back(bsize / 2 + step.x);
				else
				{
					key.code = (kuint)ch;
					Glyph* pg = data->Bind(key, osize, cache, false, qgRdh::Instance);
					_chs.push_back(pg ? pg->_adv.x + step.x : size / 2 + step.x);
				}
			}
		}

		_font->SetSize(savesize);

		return true;
	}
};


//////////////////////////////////////////////////////////////////////////
// 프리타입
KIMPL_SUBCLASS(qgFreeType, "FreeType");

//
qgFreeType::qgFreeType(bool systemfont)
	: super_type()
	, _maxcache(1024)
	, _glyphsize(_size << 6)
	, _outsize(_size + _size / 2)
	, _lock()
	, _cache(new CacheData())
{
	if (systemfont)
		AddSystemFont();
}

//
qgFreeType::~qgFreeType()
{
	delete _cache;
}

//
kint qgFreeType::GetFaceCount()
{
	return _cache->_faces.size();
}

//
kint qgFreeType::GetCacheCount()
{
	return _cache->_glps.size();
}

//
void qgFreeType::SetCacheCount(kint count)
{
	_maxcache = K_CLAMP(count, 256, 2048);

	kGuardSpinLock(_lock)
	{
		if ((kint)_cache->_glps.size() >= (_maxcache * 2))
			_cache->ClearGlyph();
	}
}

//
void qgFreeType::ClearCache()
{
	kGuardSpinLock(_lock)
		_cache->ClearGlyph();
}

//
void qgFreeType::SetSize(int size)
{
	if (size != _size)
	{
		_size = K_CLAMP(size, 2, 256);
		_glyphsize = _size << 6;
		_outsize = _size + _size / 2;
	}
}

//
tpl::Point qgFreeType::CalcPoint(kint flags, const char* str, kint len)
{
	k_return_value_if_fail(str, tpl::Point(0, 0));

	if (len < 0)
		len = K_MAX_INT - 1;

	tpl::Point pt(0, 0);
	tpl::Point pp(0, _size + _step.y);

	Key key;
	key.size = _glyphsize;
	key.style = 0;

	qgRdh* rdh = qgRdh::Instance;

	for (const char* t = str; *t != '\0';)
	{
		kint ch = k_utf8ccnc(t);
		t = k_utf8_char_next(t);

		--len;

		if (len < 0)
			break;

		if (ch == ' ')
			pp.x += _size / 2 + _step.x;
		else if (ch == '\n' || ch == '\r')
		{
			if (pt.x < pp.x)
				pt.x = pp.x;

			pp.x = 0;
			pp.y += _size + _step.y;
		}
		else if (ch == '\001' || ch == '\003' || ch == '\004')
			continue;
		else if (ch == '\002')
		{
			kint cnt = 0;

			for (++t; cnt < 6; ++t)
			{
				if (*t == '\0')
					goto pos_exit;
				else if (*t == '\v')
					break;
				else if (*t >= '0' && *t <= '9')
					cnt++;
				else if (*t >= 'A' && *t <= 'F')
					cnt++;
				else if (*t >= 'a' && *t <= 'f')
					cnt++;
				else
					break;
			}

			if (cnt > 0)
				--t;
		}
		else
		{
			key.code = (kuint)ch;

			kGuardSpinLock(_lock)
			{
				Glyph* pg = _cache->Bind(key, _outsize, _maxcache, false, rdh);

				if (!pg)
					pp.x += _size / 2 + _step.x;
				else
					pp.x += pg->_adv.x + _step.x;
			}
		}
	}

pos_exit:
	if (pt.x < pp.x)
		pt.x = pp.x;

	pt.y = pp.y;

	return pt;
}

//
tpl::Point qgFreeType::CalcPointL(kint flags, const kwchar* uni, kint len)
{
	k_return_value_if_fail(uni, tpl::Point(0, 0));

	if (len < 0)
		len = K_MAX_INT - 1;

	tpl::Point pt(0, 0);
	tpl::Point pp(0, _size + _step.y);

	Key key;
	key.size = _glyphsize;
	key.style = 0;

	qgRdh* rdh = qgRdh::Instance;

	for (; *uni != L'\0'; uni++)
	{
		kwchar ch = *uni;

		--len;

		if (len < 0)
			break;

		if (ch == L' ')
			pp.x += _size / 2 + _step.x;
		else if (ch == L'\n' || ch == L'\r')
		{
			if (pt.x < pp.x)
				pt.x = pp.x;

			pp.x = 0;
			pp.y += _size + _step.y;
		}
		else if (ch == L'\001' || ch == L'\003' || ch == L'\004')
			continue;
		else if (ch == L'\002')
		{
			kint cnt = 0;

			for (++uni; cnt < 6; ++uni)
			{
				if (*uni == L'\0')
					goto pos_exit;
				else if (*uni == L'\v')
					break;
				else if (*uni >= L'0' && *uni <= L'9')
					cnt++;
				else if (*uni >= L'A' && *uni <= L'F')
					cnt++;
				else if (*uni >= L'a' && *uni <= L'f')
					cnt++;
				else
					break;
			}

			if (cnt > 0)
				--uni;
		}
		else
		{
			key.code = (kuint)ch;

			kGuardSpinLock(_lock)
			{
				Glyph* pg = _cache->Bind(key, _outsize, _maxcache, false, rdh);

				if (!pg)
					pp.x += _size / 2 + _step.x;
				else
					pp.x += pg->_adv.x + _step.x;
			}
		}
	}

pos_exit:
	if (pt.x < pp.x)
		pt.x = pp.x;

	pt.y = pp.y;

	return pt;
}

//
void qgFreeType::TextDraw(const tpl::Rect& bound, kint flags, const char* str)
{
	k_return_if_fail(str);

	//
	qgRdh* rdh = qgRdh::Instance;

	rdh->GlyphBegin(NULL);

	//
	kint bsize = _size;
	tpl::Point pt(bound.left, bound.top + (kint)((float)bsize * 0.75f));	// 잠만 음.. 0.75가 어디서 나온 수였지?
	tpl::ColorValue cr(_color);

	Key key;
	key.size = _glyphsize;
	key.style = _style;

	//
	for (const char* t = str; *t != '\0';)
	{
		kint ch = k_utf8ccnc(t);
		t = k_utf8_char_next(t);

		if (ch == ' ')
		{
			pt.x += bsize / 2 + _step.x;
			// 공백은 그리는게 아니므로 그리지 않으므로 가로 검사 안함
			// 원래 ' '의 horiAdvance 또는 advance.x를 써야 하지만.. 음...
		}
		else if (ch == '\n')
		{
			pt.x = bound.left;
			pt.y += bsize + _step.y;

			// 세로로 빠져 나옴?
			if (pt.y > bound.bottom)
				break;
		}
		else if (ch == '\r')
		{
			pt.x = bound.left;
		}
		else if (ch == '\001')
		{
			key.style = 0;
			cr = _color;
		}
		else if (ch == '\002')
		{
			kint cnt = 0;
			kchar tmp[10];

			for (++str; cnt < 6; ++str)
			{
				if (*str == '\0')
					goto pos_exit;
				else if (*str == '\v')
					break;
				else if (*str >= '0' && *str <= '9')
					tmp[cnt++] = *str;
				else if (*str >= 'A' && *str <= 'F')
					tmp[cnt++] = *str;
				else if (*str >= 'a' && *str <= 'f')
					tmp[cnt++] = *str;
				else
					break;
			}

			if (cnt > 0)
			{
				tmp[cnt] = '\0';
				cnt = 0xFF000000 | k_atoi_h(tmp);
				cr.Set(cnt);
				--str;
			}
		}
		else if (ch == '\003')
		{
			K_UFLAG(&key.style, QGFNTS_BOLD,
				K_OFLAG(key.style, QGFNTS_BOLD) ? false : true);
		}
		else if (ch == '\004')
		{
			K_UFLAG(&key.style, QGFNTS_ITALIC,
				K_OFLAG(key.style, QGFNTS_ITALIC) ? false : true);
		}
		else
		{
			key.code = (kuint)ch;

			kGuardSpinLock(_lock)
			{
				Glyph* pg = _cache->Bind(key, _outsize, _maxcache, true, rdh);

				if (!pg)
				{
					// 글자가 없으면 공백으로 둠
					pt.x += bsize / 2 + _step.x;
				}
				else
				{
					kint x = pt.x + pg->_adv.x + _step.x;

					// 가로 검사 함
					if (pg->_buf && x < bound.right)	// buf가 있다는 것은 텍스쳐를 만들고 그릴 수 있다는 뜻
					{
						if (!pg->_tex)
							pg->Build(rdh);

						rdh->GlyphDraw(
							pt.x, pt.y - pg->_wh.y, pg->_drawsize.width, pg->_drawsize.height,
							cr, pg->_tex);
					}

					pt.x = x;
				}
			}
		}
	}

pos_exit:
	rdh->GlyphEnd();
}

//
void qgFreeType::TextDrawL(const tpl::Rect& bound, kint flags, const kwchar* uni)
{
	k_return_if_fail(uni);

	//
	qgRdh* rdh = qgRdh::Instance;

	rdh->GlyphBegin(NULL);

	//
	kint bsize = _size;
	tpl::Point pt(bound.left, bound.top + (kint)((float)bsize * 0.75f));	// 잠만 음.. 0.75가 어디서 나온 수였지?
	tpl::ColorValue cr(_color);

	Key key;
	key.size = _glyphsize;
	key.style = _style;

	//
	for (; *uni != L'\0'; uni++)
	{
		kwchar ch = *uni;

		if (ch == L' ')
		{
			pt.x += bsize / 2 + _step.x;
			// 공백은 그리는게 아니므로 그리지 않으므로 가로 검사 안함
			// 원래 ' '의 horiAdvance 또는 advance.x를 써야 하지만.. 음...
		}
		else if (ch == L'\n')
		{
			pt.x = bound.left;
			pt.y += (bsize + _step.y);

			// 세로로 빠져 나옴?
			if (pt.y > bound.bottom)
				break;
		}
		else if (ch == L'\r')
		{
			pt.x = bound.left;
		}
		else if (ch == L'\001')
		{
			key.style = 0;
			cr = _color;
		}
		else if (ch == L'\002')
		{
			kint cnt = 0;
			kwchar tmp[10];

			for (++uni; cnt < 6; ++uni)
			{
				if (*uni == L'\0')
					goto pos_exit;
				else if (*uni == L'\v')
					break;
				else if (*uni >= L'0' && *uni <= L'9')
					tmp[cnt++] = *uni;
				else if (*uni >= L'A' && *uni <= L'F')
					tmp[cnt++] = *uni;
				else if (*uni >= L'a' && *uni <= L'f')
					tmp[cnt++] = *uni;
				else
					break;
			}

			if (cnt > 0)
			{
				tmp[cnt] = L'\0';
				cnt = 0xFF000000 | k_wtoi_h(tmp);
				cr.Set(cnt);
				--uni;
			}
		}
		else if (ch == L'\003')
		{
			K_UFLAG(&key.style, QGFNTS_BOLD,
				K_OFLAG(key.style, QGFNTS_BOLD) ? false : true);
		}
		else if (ch == L'\004')
		{
			K_UFLAG(&key.style, QGFNTS_ITALIC,
				K_OFLAG(key.style, QGFNTS_ITALIC) ? false : true);
		}
		else
		{
			key.code = (kuint)ch;

			kGuardSpinLock(_lock)
			{
				Glyph* pg = _cache->Bind(key, _outsize, _maxcache, true, rdh);

				if (!pg)
				{
					// 글자가 없으면 공백으로 둠
					pt.x += bsize / 2 + _step.x;
				}
				else
				{
					kint x = pt.x + pg->_adv.x + _step.x;

					// 가로 검사 함
					if (pg->_buf && x < bound.right)	// buf가 있다는 것은 텍스쳐를 만들고 그릴 수 있다는 뜻
					{
						if (!pg->_tex)
							pg->Build(rdh);

						rdh->GlyphDraw(
							pt.x, pt.y - pg->_wh.y, pg->_drawsize.width, pg->_drawsize.height,
							cr, pg->_tex);
					}

					pt.x = x;
				}
			}
		}
	}

pos_exit:
	rdh->GlyphEnd();
}

//
qgScribeFont* qgFreeType::BuildScribe()
{
#if 0
	ScribeData* scribe = kobj_new(ScribeData, this);
#else
	ScribeData* scribe = kobj_create(ScribeData);
	scribe->_font = this;
	this->Load();
#endif
	return scribe;
}

//
bool qgFreeType::AddFont(const char* filename, bool preload/*=false*/)
{
	k_return_value_if_fail(filename, false);

	qgStub* stub = qgRdh::Instance->GetStub();

	Face face;
	memset(&face, 0, sizeof(Face));

	if (!face.Find(stub, filename, preload))
	{
		kobj_tracef(KLV_WARNING, "cannot found font: %s", filename);
		return false;
	}

	if (!face.Open(_cache->_lib))
	{
		kobj_tracef(KLV_WARNING, "font load error: %s", filename);
		return false;
	}

	kGuardSpinLock(_lock)
		_cache->_faces.push_back(face);

	return true;
}

//
bool qgFreeType::AddSystemFont()
{
#if _SB_WINDOWS_		
	if (k_sisd_ossub() < KSIPFS_WINDOWS_VISTA)
	{
		AddFont("tahoma.ttf");
		AddFont("gulim.ttc");
		return true;
	}
	else
	{
		if (!AddFont("segoeui.ttf"))
			AddFont("tahoma.ttf");

		if (!AddFont("malgun.ttf"))
			AddFont("gulim.ttc");

		return AddFont("meiryo.ttc") &&
			AddFont("simsun.ttc");
	}
#elif _SB_ANDROID_
	if (!AddFont("DroidSans.ttf"))
		AddFont("DroidSerif-Regular.ttf");

	AddFont("NanumGothic.ttf");

	// 중국어는?

	// 일본어는?

	return true;
#elif _SB_BSD_
	if (!AddFont("Droid/DroidSans.ttf"))
		if (!AddFont("dejavu/DejaVuSans.ttf"))
			if (!AddFont("bitstream-vera/Vera.ttf"))
				AddFont("freefont/FreeSans.ttf");

	if (!AddFont("nanum-otf/NanumGothic.otf"))
		if (!AddFont("nanum-ttf/NanumGothic.ttf"))
			if (!AddFont("unfonts-core/UnDotum.ttf"))
				AddFont("alee-ttf/Eunjin.ttf");

	if (!AddFont("Droid/DroidSansJapanese.ttf"))
		AddFont("takao-gothic/TakaoPGothic.ttf");

	return true;
#elif _SB_LINUX_
	if (!AddFont("ubuntu-font-family/Ubuntu-R.ttf"))
		if (!AddFont("droid/DroidSans.ttf"))
			if (!AddFont("ttf-dejavu/DejaVuSans.ttf"))
				AddFont("freefont/FreeSans.ttf");

	if (!AddFont("nanum/NanumGothic.ttf"))
		AddFont("BaekMuk/BaekMuk.ttf");

	if (!AddFont("droid/DroidSansJapanese.ttf"))
		AddFont("takao-gothic/TakaoPGothic.ttf");

	return true;
#else
#error fix me
#endif
}

//
qgFreeType* qgFreeType::New(bool systemfont/*=true*/)
{
	qgFreeType* self = kobj_new(qgFreeType, systemfont);
	return self;
}

